#pragma once
#include <iostream>
#include <chrono>
#include <thread>
#include "../common/net.hpp"
#include "../common/dispatcher.hpp"
#include "requestor.hpp"
#include "rpc_caller.hpp"
#include"rpc_registry.hpp"
#include"rpc_topic.hpp"

           
namespace RPC_project
{
    namespace client
    {
        //只能发送服务注册的信息,只会接收到serviceresponse
        class RegistryClient
        {
            public:
                using ptr =std::shared_ptr<RegistryClient>;
                //构造函数传入注册中心的地址信息 ，用于连接注册中心 ,连接后可向注册中心发送服务注册的request
                RegistryClient(const std::string &ip ,int port)
                :_provider(std::make_shared<client::Provider>(_requestor))
                ,_requestor(std::make_shared<client::Requestor>())
                ,_dispather(std::make_shared<Dispather>())
                {
                    auto service_response_cb = std::bind(&client::Requestor::onResponse, _requestor.get()
                        , std::placeholders::_1, std::placeholders::_2);
                    _dispather->RegisterHandler<BaseMessage>(MType::RSP_SERVICE, service_response_cb);

                    auto message_cb = std::bind(&Dispather::onMessage, _dispather.get()
                        , std::placeholders::_1, std::placeholders::_2);
                    _client = ClientFactory::create(ip, port);
                    _client->SetMessageCallback(message_cb);
                    _client->Connect();

                }
                //向外提供的服务注册接口 ，host为输入参数
                bool registryMethod(const std::string& method ,const Address& host)
                {
                    return _provider->registryMethod(_client->Connection() ,method ,host);
                }
            private:
                Requestor::ptr  _requestor;//请求的管理模块
                client::Provider::ptr _provider;
                Dispather::ptr _dispather;//消息分发处理
                BaseClient::ptr _client;

        };

        //服务发现中途会给我们进行通知，会接收到servicerequest
        class DiscoveryClient
        {
            public:
                using ptr =std::shared_ptr<DiscoveryClient>;
                //构造函数传入注册中心的地址信息 ，用于连接注册中心 ,连接后可向注册中心发送服务发现的request
                DiscoveryClient(const std::string &ip ,int port, const Discovery::OfflineCallback &cb)
                :_requestor(std::make_shared<client::Requestor>())
                ,_dispather(std::make_shared<Dispather>())
                ,_discoverer(std::make_shared<client::Discovery>(_requestor ,cb))
                {
                     auto service_response_cb = std::bind(&client::Requestor::onResponse, _requestor.get()
                        , std::placeholders::_1, std::placeholders::_2);
                    _dispather->RegisterHandler<BaseMessage>(MType::RSP_SERVICE, service_response_cb);
                    //服务上线下线通知的处理
                    auto service_request_cb =std::bind(&client::Discovery::onServiceRequest ,_discoverer.get() 
                            , std::placeholders::_1, std::placeholders::_2);
                    _dispather->RegisterHandler<ServiceRequest>(MType::REQ_SERVICE, service_request_cb);



                    auto message_cb = std::bind(&Dispather::onMessage, _dispather.get()
                        , std::placeholders::_1, std::placeholders::_2);
                    _client = ClientFactory::create(ip, port);
                    _client->SetMessageCallback(message_cb);
                    _client->Connect();
                }

                //向外提供的服务发现接口 ,host为输出参数
                bool serviceDiscovery(std::string method, Address &host)
                {
                    return _discoverer->serviceDiscovery(_client->Connection(), method, host);
                }
            private:
                
                Requestor::ptr  _requestor;//请求的管理模块
                client::Discovery::ptr _discoverer;
                Dispather::ptr _dispather;//消息分发处理
                BaseClient::ptr _client;
        };

        //rpc客户端应该实现的是rpc调用功能
        //先进行服务发现，再去进行rpc调用
        //rpc客户端用常连接还是短连接
        //长连接：在客户端（RpcClient）中有一个连接池，在第一次实例化客户端（RpcClient）进行调用后，并不会关闭客户端，后续发起RPC调用后,将客户端放入池中
        //如果循环到这个客户端上次用到的端口了 ，在连接池中使用已连接好的客户端（RpcClient），直接取出客户端对象使用发起RPC调用
        //难点：rpc提供者一旦下线 ，需要从池子中将对应的客户端（RpcClient）删除
        
        //短连接：每当要进行Rpc调用时,先获取服务提供者的主机地址,然后会创建一个客户端RpcClient出来，连接服务提供者之后,进行RPC调用,调用后,释放客户端
        //决定用长链接,大部分服务器注重效率,长连接可以复用
        class RpcClient 
        {
            //RpcClient如果启用了服务发现 ，他就是两个客户端 ，一个是rpc调用客户端 ，一个是服务发现客户端 
            public:
                using ptr =std::shared_ptr<RpcClient>;

                //enableDiscovery表示是否启用服务发现功能 ，也决定了传入的地址是注册中心的地址(启用服务发现) ，还是服务提供者的地址
                RpcClient(bool enableDiscovery , const std::string &ip ,int port)
                    :_enableDiscovery(enableDiscovery)
                    ,_requestor(std::make_shared<client::Requestor>())
                    ,_dispather(std::make_shared<Dispather>())
                    ,_rpc_caller(std::make_shared<RpcCaller>(_requestor))
                {
                    //针对rpc_response的回调处理
                    auto rpc_response_cb = std::bind(&client::Requestor::onResponse, _requestor.get()
                        , std::placeholders::_1, std::placeholders::_2);
                    _dispather->RegisterHandler<BaseMessage>(MType::RSP_RPC, rpc_response_cb);

                    //启用了服务发现 ,传入的地址是注册中心的地址,是服务发现客户端需要连接的地址,通过地址实例化DiscoveryClient
                    //没有启用服务发现 ,地址为服务提供者的地址,直接实例化RpcClient
                    if(enableDiscovery)
                    {
                        //下线时的删除客户端的回调
                        auto offline_cb =std::bind(&RpcClient::delClient ,this ,std::placeholders::_1);
                        _discovery_client = std::make_shared<DiscoveryClient>(ip ,port, offline_cb);
                    }
                    else
                    {
                        auto message_cb = std::bind(&Dispather::onMessage, _dispather.get()
                        , std::placeholders::_1, std::placeholders::_2);
                        _rpc_client = ClientFactory::create(ip, port);
                        _rpc_client->SetMessageCallback(message_cb);
                        _rpc_client->Connect();

                    }

                }

                //同步
                bool call(const std::string &method, const Json::Value params, Json::Value &result)
                {
                    //1.找到服务提供者
                    //启用了服务发现 
                    BaseClient::ptr client=getClient(method);
                    if(client.get() == nullptr)
                    {
                        return false;
                    }
                    
                    //3.通过客户端连接发送rpc请求(caller发送rpc请求)
                    return _rpc_caller->call(client->Connection() ,method ,params ,result);
                }
                //异步
                bool call(const std::string &method,  const Json::Value params, std::future<Json::Value> &result)
                {
                    BaseClient::ptr client=getClient(method);
                    if(client.get() == nullptr)
                    {
                        return false;
                    }
                    
                    //3.通过客户端连接发送rpc请求(caller发送rpc请求)
                    return _rpc_caller->call(client->Connection() ,method ,params ,result);
                }
                //异步回调
                bool call(const std::string &method, const Json::Value params, const RpcCaller::JsonResponseCallback &cb)
                {
                    BaseClient::ptr client=getClient(method);
                    if(client.get() == nullptr)
                    {
                        return false;
                    }
                    
                    //3.通过客户端连接发送rpc请求(caller发送rpc请求)
                    return _rpc_caller->call(client->Connection() ,method ,params ,cb);
                }
            private:
                BaseClient::ptr newClient(const Address &host)
                {
                        auto message_cb = std::bind(&Dispather::onMessage, _dispather.get()
                            , std::placeholders::_1, std::placeholders::_2);
                        auto client = ClientFactory::create(host.first, host.second);
                        client->SetMessageCallback(message_cb);
                        client->Connect();
                        // std::unique_lock<std::mutex> lock(_mutex);// 在这里其实已经加锁了
                        putClient(host ,client);

                        return client;
                }
                BaseClient::ptr getClient(const Address &host)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it =_rpc_clients.find(host);
                    if(it == _rpc_clients.end())
                    {
                        return BaseClient::ptr();
                    }
                    return it->second;
                }

                BaseClient::ptr getClient(const std::string &method)
                {
                    //1.找到服务提供者
                    //启用了服务发现 
                    BaseClient::ptr client;
                    if(_enableDiscovery)
                    {
                        //1.通过服务发现,获取服务提供者地址信息 
                        Address host;
                        bool ret =_discovery_client->serviceDiscovery(method , host);
                        if(ret =false)
                        {
                            ELOG("当前%s服务 ,没有找到服务提供者", method.c_str());
                            return  BaseClient::ptr();
                        }
                        //2.查看服务提供者是否已经有实例化的客户端 ,有就直接使用 ,没有就创建
                        client = getClient(host);
                        if(client.get() == nullptr)
                        {
                            //没找到已经实例化的客户端就创建
                            client =newClient(host);
                        }
                    }
                    else//非服务发现
                    {
                        client=_rpc_client;
                    }
                    return client;
                }
                void putClient(const Address &host , BaseClient::ptr &client)
                {
                    std::unique_lock<std::mutex> lock(_mutex); // 二次加锁导致死锁
                    _rpc_clients.insert(std::make_pair(host ,client));
                }
                void delClient(const Address &host)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _rpc_clients.erase(host);
                }
            private:
                struct AddressHash
                {
                    size_t operator()(const Address & host) const
                    {
                        std::string address = host.first +std::to_string(host.second);
                        return std::hash<std::string>{}(address);
                    }
                } ;
                bool _enableDiscovery;
                DiscoveryClient::ptr _discovery_client;
                Requestor::ptr  _requestor;//请求的管理模块
                Dispather::ptr _dispather;//消息分发处理
                RpcCaller::ptr _rpc_caller;
                BaseClient::ptr _rpc_client;//用于未启用服务发现时的客户端
                std::mutex _mutex;
                //通过服务发现得到一个应该请求的主机地址 ,通过地址来_rpc_clients中查找客户端
                //没找到 就创建客户端 ,设置回调添加到_rpc_clients
                //找到了 直接发起调用
                
                //<Address , client>
                //Address 是自定义类型 需要能够计算hash值
                std::unordered_map <Address , BaseClient::ptr ,AddressHash> _rpc_clients;//用服务发现的客户端连接池

        };

        class SubscribePublishClient
        {
            public:
                SubscribePublishClient(const std::string ip ,int port)//服务器的ip地址和端口
                    :_requestor(std::make_shared<client::Requestor>())
                    ,_dispather(std::make_shared<Dispather>())
                    ,_topic_manager(std::make_shared<TopicManager>(_requestor))
                {
                    //客户端对接受到的topic_response的处理
                    auto  topic_response_cb = std::bind(&Requestor::onResponse, _requestor.get()
                        , std::placeholders::_1, std::placeholders::_2);
                    _dispather->RegisterHandler<BaseMessage>(MType::RSP_TOPIC, topic_response_cb);

                    //客户端收到主题消息推送的request的回调函数
                    auto  topic_request_cb = std::bind(&TopicManager::onPublishRequest ,_topic_manager.get() 
                        , std::placeholders::_1, std::placeholders::_2);
                    _dispather->RegisterHandler<TopicRequest>(MType::REQ_TOPIC, topic_request_cb);//???


                        auto message_cb = std::bind(&Dispather::onMessage, _dispather.get()
                        , std::placeholders::_1, std::placeholders::_2);
                        _rpc_client = ClientFactory::create(ip, port);
                        _rpc_client->SetMessageCallback(message_cb);
                        _rpc_client->Connect();
                }

                //提供给用户的5个接口
                bool creatTopic(const std::string &key)
                {
                    return _topic_manager->creatTopic(_rpc_client->Connection(),key);
                }
                bool removeTopic(const std::string &key)
                {
                   return _topic_manager->removeTopic(_rpc_client->Connection(),key);
                }
                bool subscribeTopic(const std::string &key,const TopicManager::subCallback& cb)//订阅主题 ，设置收到消息后的回调函数
                {
                    return _topic_manager->subscribeTopic(_rpc_client->Connection(),key ,cb);
                }
                bool cannel(const std::string &key)//取消一个主题的订阅
                {
                  return _topic_manager->cannel(_rpc_client->Connection(),key);
                }
                bool publish(const std::string &key ,const std::string& message)
                {
                    return _topic_manager->publish(_rpc_client->Connection(),key ,message);
                }

                void shutdown()
                {
                    _rpc_client->Shutdown();
                }

            private:
                Requestor::ptr  _requestor;//请求的管理模块
                TopicManager::ptr _topic_manager;
                Dispather::ptr _dispather;//消息分发处理
                BaseClient::ptr _rpc_client;//用于未启用服务发现时的客户端
        };


    }

}

